import type { MaybeRef, PointerType, Position } from '@vueuse/core'

export interface UseAbsoluteDraggableOptions {
  exceedable?: boolean
  /**
   * Only start the dragging when click on the element directly
   *
   * @default false
   */
  exact?: MaybeRef<boolean>
  /**
   * Prevent events defaults
   *
   * @default false
   */
  preventDefault?: MaybeRef<boolean>
  /**
   * Prevent events propagation
   *
   * @default false
   */
  stopPropagation?: MaybeRef<boolean>
  /**
   * Element to attach `pointermove` and `pointerup` events to.
   *
   * @default window
   */
  draggingElement?: MaybeRef<HTMLElement | SVGElement | Window | Document | null | undefined>
  /**
   * Handle that triggers the drag event
   *
   * @default target
   */
  handle?: MaybeRef<HTMLElement | SVGElement | null | undefined>
  /**
   * Pointer types that listen to.
   *
   * @default ['mouse', 'touch', 'pen']
   */
  pointerTypes?: PointerType[]
  /**
   * Initial position of the element.
   *
   * @default { x: 0, y: 0 }
   */
  initialValue?: Position
  /**
   * Callback when the dragging starts. Return `false` to prevent dragging.
   */
  onStart?: (position: Position, event: PointerEvent) => void | false
  /**
   * Callback during dragging.
   */
  onMove?: (position: Position, event: PointerEvent) => void
  /**
   * Callback when dragging end.
   */
  onEnd?: (position: Position, event: PointerEvent) => void
}
/**
 * useDraggable是针对position为fixed的拖拽，这里对存在父子关系的absolute进行特殊处理
 * @param dragTriggerElement - 拖拽触发的dom节点
 * @param dragContentElement - 要拖拽的主体dom节点
 * @param containerElement - 拖拽的主题所相对定位的父级dom节点
 * @param options  - 拖拽触发的dom节点 - 拖拽触发的dom节点
 */
export function useAbsoluteDraggable(dragTriggerElement: MaybeRef<HTMLElement | null | undefined>, dragContentElement: MaybeRef<HTMLElement | string | null | undefined>, containerElement: MaybeRef<HTMLElement | string | null | undefined>, options?: UseAbsoluteDraggableOptions) {
  const result = useDraggable(dragTriggerElement, options)
  if (unref(dragTriggerElement)) {
    unref(dragTriggerElement)!.style.userSelect = 'none'
  }

  function deepAddParentOffset(divEl: HTMLElement | undefined, key: 'offsetTop' | 'offsetLeft', offset: number = 0) {
    if (divEl?.[key]) {
      offset += divEl?.[key]
    }
    if (divEl?.offsetParent) {
      return deepAddParentOffset(divEl.offsetParent as HTMLElement, key, offset)
    }
    else {
      return offset
    }
  }
  // useDraggable是针对position为fixed的拖拽，这里对absolute进行特殊处理
  const styleComputed = computed(() => {
    let containerOffsetRight = 0
    let containerOffsetBottom = 0
    let containerOffsetLeft = 0
    let containerOffsetTop = 0
    let containerEl = unref(containerElement) as HTMLDivElement
    let dragContentEl = unref(dragContentElement) as HTMLDivElement
    if (typeof containerEl == 'string') {
      containerEl = document.querySelector(containerEl) as unknown as HTMLDivElement
    }
    if (typeof dragContentEl == 'string') {
      dragContentEl = document.querySelector(dragContentEl) as unknown as HTMLDivElement
    }
    if (containerEl && dragContentEl) {
      containerOffsetLeft = deepAddParentOffset(dragContentEl.offsetParent as HTMLElement, 'offsetLeft')
      containerOffsetTop = deepAddParentOffset(dragContentEl.offsetParent as HTMLElement, 'offsetTop')
      containerOffsetRight = containerOffsetLeft + containerEl.clientWidth - dragContentEl.clientWidth
      containerOffsetBottom = containerOffsetTop + containerEl.clientHeight - dragContentEl.clientHeight
      try {
        // style.value : left:0px;top:0px;
        const left = Number(result.style.value?.slice(result.style.value?.indexOf(':') + 1, result.style.value?.indexOf('px;')))
        const top = Number(result.style.value?.slice(result.style.value?.lastIndexOf(':') + 1, result.style.value?.lastIndexOf('px;')))
        if (left == options?.initialValue?.x && top == options?.initialValue?.y) {
          return result.style.value
        }
        let computedLeft = 0
        if (options?.exceedable) {
          computedLeft = left - containerOffsetLeft
        }
        else {
          if (left - containerOffsetLeft > 0) {
            if (left - containerOffsetRight > 0) {
              computedLeft = containerEl.clientWidth - dragContentEl.clientWidth
            }
            else {
              computedLeft = left - containerOffsetLeft
            }
          }
          else {
            computedLeft = 0
          }
        }
        let computedTop = 0
        if (options?.exceedable) {
          computedTop = top - containerOffsetTop
        }
        else {
          if (top - containerOffsetTop > 0) {
            if (top - containerOffsetBottom > 0) {
              computedTop = containerEl.clientHeight - dragContentEl.clientHeight
            }
            else {
              computedTop = top - containerOffsetTop
            }
          }
          else {
            computedTop = 0
          }
        }
        return `left:${computedLeft}px !important;top:${computedTop}px !important;`
      }
      catch {
        return result.style.value
      }
    }
    else {
      return result.style.value
    }
  })
  return {
    ...result,
    styleComputed,
  }
}
